using System;
using System.Diagnostics;
using System.Net;
using System.Text;

namespace Pegasus.Diagnostics
{
	/// <summary>
	/// Summary description for ParamCode.
	/// </summary>
	public class ParamCode
	{
		// Local Const Values
		private const string AssertText = "Parameter {0} condition failed at {1}";
		private const string AssertNullText = "Parameter {0} is not null, at {1}";
		private const string AssertNotNullText = "Parameter {0} is null, at {1}";
		private const string AssertNotEmptyStringText = "String parameter {0} is null or empty, at {1}";

		/// <summary>
		/// Checks for a condition and displays a message if the condition is <strong>false</strong>.
		/// </summary>
		/// <param name="condition">If false argument exception will be thrown; If true the method is skipped.</param>
		/// <param name="paramName">The name of the parameter</param>
		[Conditional( "TRACE" )]
		public static void Assert( bool condition, string paramName )
		{
			if( !condition )
			{
				WriteLineAndThrow( AssertText, paramName, 1, null );
			}
		}

		/// <summary>
		/// Asserts the specified condition.
		/// </summary>
		/// <param name="condition">If false argument exception will be thrown; If true the method is skipped.</param>
		/// <param name="paramName">The name of the parameter</param>
		/// <param name="messageFormat">A message to display containing zero or more format items.</param>
		/// <param name="args">An object array containing zero or more objects to format.</param>
		[Conditional( "TRACE" )]
		public static void Assert( bool condition, string paramName, string messageFormat, params object[] args )
		{
			if( !condition )
			{
				WriteLineAndThrow( AssertText, paramName, 1, messageFormat, args );
			}
		}

		/// <summary>
		/// Checks if the given object is null and and displays a message if it is.
		/// </summary>
		/// <param name="obj">The object to check.</param>
		/// <param name="paramName">The name of the parameter</param>
		[Conditional( "TRACE" )]
		public static void AssertNull( object obj, string paramName )
		{
			if( obj != null )
			{
				WriteLineAndThrow( AssertNullText, paramName, 1, null );
			}
		}

		/// <summary>
		/// Checks if the given object is null and and displays a message if it is.
		/// </summary>
		/// <param name="obj">The object to check.</param>
		/// <param name="paramName">The name of the parameter</param>
		/// <param name="messageFormat">A message to display containing zero or more format items.</param>
		/// <param name="args">An object array containing zero or more objects to format.</param>
		[Conditional( "TRACE" )]
		public static void AssertNull( object obj, string paramName, string messageFormat, params object[] args )
		{
			if( obj != null )
			{
				WriteLineAndThrow( AssertNullText, paramName, 1, messageFormat, args );
			}
		}

		/// <summary>
		/// Checks if the given object is null and and displays a message if it is.
		/// </summary>
		/// <param name="obj">The object to check.</param>
		/// <param name="paramName">The name of the parameter</param>
		[Conditional( "TRACE" )]
		public static void AssertNotNull( object obj, string paramName )
		{
			if( obj == null )
			{
				WriteLineAndThrow( AssertNotNullText, paramName, 1, null );
			}
		}

		/// <summary>
		/// Checks if the given object is null and and displays a message if it is.
		/// </summary>
		/// <param name="obj">The object to check.</param>
		/// <param name="paramName">The name of the parameter</param>
		/// <param name="messageFormat">A message to display containing zero or more format items.</param>
		/// <param name="args">An object array containing zero or more objects to format.</param>
		[Conditional( "TRACE" )]
		public static void AssertNotNull( object obj, string paramName, string messageFormat, params object[] args )
		{
			if( obj == null )
			{
				WriteLineAndThrow( AssertNotNullText, paramName, 1, messageFormat, args );
			}
		}

		/// <summary>
		/// Checks if the given string is null or empty and and displays a message if it is.
		/// </summary>
		/// <param name="str">The string to check.</param>
		/// <param name="paramName">The name of the parameter</param>
		[Conditional( "TRACE" )]
		public static void AssertNotEmpty( string str, string paramName )
		{
			if( string.IsNullOrEmpty( str ) )
			{
				WriteLineAndThrow( AssertNotEmptyStringText, paramName, 1, null );
			}
		}

		/// <summary>
		/// Checks if the given string is null or empty and and displays a message if it is.
		/// </summary>
		/// <param name="str">The string to check.</param>
		/// <param name="paramName">The name of the parameter</param>
		/// <param name="messageFormat">A message to display containing zero or more format items.</param>
		/// <param name="args">An object array containing zero or more objects to format.</param>
		[Conditional( "TRACE" )]
		public static void AssertNotEmpty( string str, string paramName, string messageFormat, params object[] args )
		{
			if( string.IsNullOrEmpty( str ) )
			{
				WriteLineAndThrow( AssertNotEmptyStringText, paramName, 1, messageFormat, args );
			}
		}

		/// <summary>
		/// Checks if the given Guid is empty and and displays a message if it is.
		/// </summary>
		/// <param name="guid">The Guid to check.</param>
		/// <param name="paramName">The name of the parameter</param>
		[Conditional( "TRACE" )]
		public static void AssertNotEmpty( Guid guid, string paramName )
		{
			if( guid == Guid.Empty )
			{
				string msg = string.Format( "GUID parameter {0} is empty, at {1}", paramName, TraceDumper.GetCallingMethod( new StackFrame( 1, true ) ) );
				Trace.WriteLine( msg );
				throw new ArgumentException( msg );
			}
		}

		/// <summary>
		/// Checks if the given IPAddress is empty and and displays a message if it is.
		/// </summary>
		/// <param name="ipAddress">The IPAddress to check.</param>
		/// <param name="paramName">The name of the parameter</param>
		[Conditional( "TRACE" )]
		public static void AssertNotEmpty( IPAddress ipAddress, string paramName )
		{
			if( ipAddress == null || ipAddress == IPAddress.None )
			{
				string msg = string.Format( "IPAddress parameter {0} is null or empty (IPAddress.None), at {1}", paramName, TraceDumper.GetCallingMethod( new StackFrame( 1, true ) ) );
				Trace.WriteLine( msg );
				throw new ArgumentException( msg );
			}
		}

		/// <summary>
		/// The string can be null or if it has a value it can't be empty.
		/// </summary>
		/// <param name="str">The string to check.</param>
		/// <param name="paramName">The name of the parameter</param>
		[Conditional( "TRACE" )]
		public static void AssertNullOrNotEmpty( string str, string paramName )
		{
			if( str != null && str.Length == 0 )
			{
				string msg = string.Format( "String parameter {0} is null or empty, at {1}", paramName, TraceDumper.GetCallingMethod( new StackFrame( 1, true ) ) );
				Trace.WriteLine( msg );
				throw new ArgumentException( msg );
			}
		}

		/// <summary>
		/// Checks if the given int is between the min and max values and displays a message if it is not.
		/// </summary>
		/// <param name="value">Int value to check.</param>
		/// <param name="min">Minimum value.</param>
		/// <param name="max">Maximum value.</param>
		/// <param name="paramName">The name of the parameter</param>
		[Conditional( "TRACE" )]
		public static void AssertRange( int value, int min, int max, string paramName )
		{
			if( value < min || value > max )
			{
				string msg = string.Format( "Int32 parameter {0} is out of range {1} < {2} < {3}, at {4}", 
					paramName, min, value, max,
					TraceDumper.GetCallingMethod( new StackFrame( 1, true ) ) );

				Trace.WriteLine( msg );
				throw new ArgumentException( msg );
			}
		}

		/// <summary>
		/// Checks if the given int is between the min and max values and displays a message if it is not.
		/// </summary>
		/// <param name="value">Int value to check.</param>
		/// <param name="min">Minimum value.</param>
		/// <param name="max">Maximum value.</param>
		/// <param name="paramName">The name of the parameter</param>
		[Conditional( "TRACE" )]
		public static void AssertRange( long value, long min, long max, string paramName )
		{
			if( value < min || value > max )
			{
				string msg = string.Format( "Long parameter {0} is out of range {1} < {2} < {3}, at {4}", 
					paramName, min, value, max,
					TraceDumper.GetCallingMethod( new StackFrame( 1, true ) ) );

				Trace.WriteLine( msg );
				throw new ArgumentException( msg );
			}
		}

		/// <summary>
		/// Checks if the given int is between the min and max values and displays a message if it is not.
		/// </summary>
		/// <param name="value">Int value to check.</param>
		/// <param name="min">Minimum value.</param>
		/// <param name="max">Maximum value.</param>
		/// <param name="paramName">The name of the parameter</param>
		[Conditional( "TRACE" )]
		public static void AssertRange( double value, double min, double max, string paramName )
		{
			if( value < min || value > max )
			{
				string msg = string.Format( "Double parameter {0} is out of range {1} < {2} < {3}, at {4}", 
					paramName, min, value, max,
					TraceDumper.GetCallingMethod( new StackFrame( 1, true ) ) );

				Trace.WriteLine( msg );
				throw new ArgumentException( msg );
			}
		}

		/// <summary>
		/// Writes the line and throw.
		/// </summary>
		/// <param name="parameterMessage">The parameter message as to why the thing failed.</param>
		/// <param name="paramName">Name of the parameter.</param>
		/// <param name="skipStackFrames">The skip stack frames.</param>
		/// <param name="messageFormat">The message format to be appended to the end of the parameter message.</param>
		/// <param name="args">The args for the message format.</param>
		private static void WriteLineAndThrow( string parameterMessage, string paramName, int skipStackFrames, string messageFormat, params object[] args )
		{
			StringBuilder msg = new StringBuilder();

			// Build the parameter string 
			msg.AppendFormat( parameterMessage, paramName, TraceDumper.GetCallingMethod( new StackFrame( skipStackFrames + 1, true ) ) );

			if( !string.IsNullOrEmpty( messageFormat ) )
			{
				msg.Append( " " );
				msg.AppendFormat( messageFormat, args );
			}

			string message = msg.ToString();

			Trace.WriteLine( message );
			throw new ArgumentException( message );
		}
	}
}
